/*
 *   This is a NT Service, which pings a number of sites in a random order
 *   to keep your internet connection alive.
 *   
 *   Copyright (C) 2004  Jess Nielsen (jnielsen@freesiteuk.co.uk)
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <iostream.h>
#include <winsock.h>
#include <windowsx.h>
#include "icmpdefs.h"
#include "stdafx.h"
#include "FileLogging.h"
#include "importado.h"

char *	gpConfigFile	= NULL;
char *	gpDestination	= NULL;
long	gWaiting		= 0;
int		gRoundTrip		= 0;
int		gLogAll			= 0;

int doit(int argc, char* argv[])
{
    // Check for correct command-line args
    if (argc < 2) {
		CFileLogging::get_FileLogging()->WriteToFile("usage: ping <host>");
        return 1;
    }
    
    // Load the ICMP.DLL
    HINSTANCE hIcmp = LoadLibrary("ICMP.DLL");
    if (hIcmp == 0) {
		CFileLogging::get_FileLogging()->WriteToFile("Unable to locate ICMP.DLL!");
        return 2;
    }

    // Look up an IP address for the given host name
    struct hostent* phe;
    if ((phe = gethostbyname(argv[1])) == 0) {
		char temp[255];
		strcpy(temp, "Could not find IP address for ");
		strcat(temp, argv[1]);
		CFileLogging::get_FileLogging()->WriteToFile(temp, TRUE);
        return 3;
    }

    // Get handles to the functions inside ICMP.DLL that we'll need
    typedef HANDLE (WINAPI* pfnHV)(VOID);
    typedef BOOL (WINAPI* pfnBH)(HANDLE);
    typedef DWORD (WINAPI* pfnDHDPWPipPDD)(HANDLE, DWORD, LPVOID, WORD,
            PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD); // evil, no?
    pfnHV pIcmpCreateFile;
    pfnBH pIcmpCloseHandle;
    pfnDHDPWPipPDD pIcmpSendEcho;
    pIcmpCreateFile = (pfnHV)GetProcAddress(hIcmp,
            "IcmpCreateFile");
    pIcmpCloseHandle = (pfnBH)GetProcAddress(hIcmp,
            "IcmpCloseHandle");
    pIcmpSendEcho = (pfnDHDPWPipPDD)GetProcAddress(hIcmp,
            "IcmpSendEcho");
    if ((pIcmpCreateFile == 0) || (pIcmpCloseHandle == 0) || 
            (pIcmpSendEcho == 0)) {
		CFileLogging::get_FileLogging()->WriteToFile("Failed to get proc addr for function.");
        return 4;
    }

    // Open the ping service
    HANDLE hIP = pIcmpCreateFile();
    if (hIP == INVALID_HANDLE_VALUE) {
		CFileLogging::get_FileLogging()->WriteToFile("Unable to open ping service.");
        return 5;
    }
   
    // Build ping packet
    char acPingBuffer[64];
    memset(acPingBuffer, '\xAA', sizeof(acPingBuffer));
    PIP_ECHO_REPLY pIpe = (PIP_ECHO_REPLY)GlobalAlloc(
            GMEM_FIXED | GMEM_ZEROINIT,
            sizeof(IP_ECHO_REPLY) + sizeof(acPingBuffer));
    if (pIpe == 0) {
		CFileLogging::get_FileLogging()->WriteToFile("Failed to allocate global ping packet buffer.");
        return 6;
    }
    pIpe->Data = acPingBuffer;
    pIpe->DataSize = sizeof(acPingBuffer);      

    // Send the ping packet
    DWORD dwStatus = pIcmpSendEcho(hIP, *((DWORD*)phe->h_addr_list[0]), 
            acPingBuffer, sizeof(acPingBuffer), NULL, pIpe, 
            sizeof(IP_ECHO_REPLY) + sizeof(acPingBuffer), 5000);
    if (dwStatus != 0) {
		char value[16];
		char temp[255 * 255];
		memset(temp, 0, 255 * 255);
		strcpy(temp, "Reached addr: ");
		strcat(temp, argv[1]);
		strcat(temp, " RTT: ");
		itoa(int(pIpe->RoundTripTime), value, 10);
		strcat(temp, value);
		strcat(temp, " ms, ");
		strcat(temp, " TTL: ");
		itoa(int(pIpe->Options.Ttl), value, 10);
		strcat(temp, value);

		CFileLogging::get_FileLogging()->WriteToFile(temp);
    }
    else {
		/* maybe the packages are encrypted? */
		CFileLogging::get_FileLogging()->WriteToFile("Error obtaining info from ping packet.", TRUE);
		CFileLogging::get_FileLogging()->WriteToFile(argv[1], TRUE);
    }

    // Shut down...
    GlobalFree(pIpe);
    FreeLibrary(hIcmp);
    return 0;
}

int loadFileNameFromRegistry()
{
	CRegKey reg; char value[256]; DWORD count = 256;
	try {		
		reg.Open( HKEY_LOCAL_MACHINE, "SOFTWARE\\KeepAlive", KEY_READ );
		reg.QueryValue( value, "ConfigFile", &count );

		if (gpConfigFile)
			free(gpConfigFile);
			
		gpConfigFile = (char*)malloc(strlen(value)+1);
		memset(gpConfigFile, 0, strlen(value)+1);
		strcpy(gpConfigFile, value);

		DWORD count = 256;
		reg.QueryValue( value, "LogAllEnabled", &count );
		gLogAll = atol(value);
	} catch (_com_error &e) {
		CFileLogging::get_FileLogging()->WriteToFile("--- loadFileNameFromRegistry() ---", TRUE);
		CFileLogging::get_FileLogging()->WriteToFile(e.Source(), TRUE);		
		CFileLogging::get_FileLogging()->WriteToFile(e.ErrorMessage(), TRUE);
		CFileLogging::get_FileLogging()->WriteToFile(e.Description(), TRUE);
		return -1;
	} catch (...) {
		CFileLogging::get_FileLogging()->WriteToFile("--- loadFileNameFromRegistry() ---", TRUE);
		CFileLogging::get_FileLogging()->WriteToFile("Unknown exception!", TRUE);
		return -2;
	}

	return 0;
}

int loadData()
{
	HRESULT hr = S_FALSE; long len = 0, pos = 0;
	XML::IXMLDOMDocumentPtr xmlDoc = NULL;
	bstr_t err;

	try {
		xmlDoc.CreateInstance(__uuidof(XML::DOMDocument));
		hr = xmlDoc->put_async( TRUE );
	
		if (gpConfigFile)
			hr = xmlDoc->load(gpConfigFile);
		else {
			CFileLogging::LogToFile("Using default values", TRUE);
			return -1;
		}
			
		XML::IXMLDOMNodeListPtr nodelist = NULL;
		nodelist = xmlDoc->selectNodes("//destination");
		len = nodelist->length;
		
		XML::IXMLDOMNodePtr node = NULL;
		pos = (rand() % len);
		node = nodelist->item[pos];

		if (node) {
			gpDestination = (char*)malloc(strlen(node->text)+1);
			memset(gpDestination, 0, strlen(node->text)+1);
			strcpy(gpDestination, node->text);
			node = NULL;
		}

		node = xmlDoc->selectSingleNode("//period");
		if (node) {
			gWaiting = atol(node->text);
			gWaiting *= 1000;
			node = NULL;
		}
	
		xmlDoc = NULL; 

	} catch (_com_error &e) {
		CFileLogging::get_FileLogging()->WriteToFile("--- loadData()", TRUE);
		CFileLogging::get_FileLogging()->WriteToFile(e.Source(), TRUE);		
		CFileLogging::get_FileLogging()->WriteToFile(e.ErrorMessage(), TRUE);
		CFileLogging::get_FileLogging()->WriteToFile(e.Description(), TRUE);
		return -2;
	} catch (...) {
		CFileLogging::get_FileLogging()->WriteToFile("--- loadData()", TRUE);
		CFileLogging::get_FileLogging()->WriteToFile("Unknown exception!", TRUE);
		return -3;
	}

	return 0;
}


int pingAhead() 
{
    char * host[2] = { NULL, NULL };
	WSAData wsaData;
	int retval = 0;

	try {
		if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
			return 255;
		}				

		host[0] = (char*)malloc(strlen("ping")+1);
		memset(host[0], 0, strlen("ping")+1);
		strcpy((char*)host[0], "ping");

		retval = loadData();
		if (retval != 0 || NULL == gpDestination) {
			const char * pDestination = "www.tv2.dk";

			host[1] = (char*)malloc(strlen(pDestination)+1);
			memset(host[1], 0, strlen(pDestination)+1);
			strcpy((char*)host[1], pDestination);
		} else {
			if (!host[1])
				free(host[1]);

			host[1] = (char*)malloc(strlen(gpDestination)+1);
			memset(host[1], 0, strlen(gpDestination)+1);
			strcpy((char*)host[1], gpDestination);
		}
		retval = doit(2, host);

		WSACleanup();
	/*
		if (0 != gWaiting && gRoundTrip >= 3) {
			gRoundTrip = 0;
			Sleep(gWaiting);
		} else {
			gRoundTrip++;
		}
	*/
	} catch (...) {
		CFileLogging::get_FileLogging()->WriteToFile("--- pingAhead()", TRUE);
		CFileLogging::get_FileLogging()->WriteToFile("Unknown exception!", TRUE);
		retval = -1;
	}

	return retval;
}